﻿using System.Collections.Generic;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace ValidationFramework.WPF
{

	/// <summary>
	/// The default <see cref="IErrorDisplayStrategy"/>.
	/// </summary>
	/// <remarks>
	/// Will show and hide a <see cref="FrameworkElement"/>s <see cref="Validation.ErrorTemplateProperty"/>.
	/// </remarks>
	public class ErrorDisplayStrategy : IErrorDisplayStrategy
	{
		static readonly MethodInfo method;
		private readonly Dictionary<FrameworkElement, string> savedToolTips;

		static ErrorDisplayStrategy()
		{
			//HACK: should do with with a style???
			method = typeof(Validation).GetMethod("ShowValidationAdorner", BindingFlags.NonPublic | BindingFlags.Static);
		}

		/// <summary>
		/// Instantiates a new instance of the <see cref="ErrorDisplayStrategy"/> class.
		/// </summary>
		public ErrorDisplayStrategy()
		{
			savedToolTips = new Dictionary<FrameworkElement, string>();
		}


        public virtual bool CanHandleTarget(FrameworkElement frameworkElement)
        {
            return true;
        }

		/// <inheritdoc />
		public virtual void DisplayError(BindingExpressionBase bindingExpressionBase,  FrameworkElement frameworkElement, string errorMessage)
		{
			//frameworkElement.SetValue(Validation.ErrorsProperty,"asdasds");
			DisplayToolTip(frameworkElement, errorMessage);
			//Validation.MarkInvalid(bindingExpressionBase,new ValidationError(new DummyRule(), errorMessage));
			DisplayValidationAdorner(frameworkElement);
		}

		/// <inheritdoc />
		public virtual void ClearError(FrameworkElement frameworkElement)
		{
			ClearToolTip(frameworkElement);
			ClearValidationAdorner(frameworkElement);
		}

		/// <summary>
		/// Display some text as a <see cref="FrameworkElement.ToolTip"/> for a <see cref="FrameworkElement"/>.
		/// </summary>
		/// <remarks>
		/// The existing <see cref="FrameworkElement.ToolTip"/> will be cached and can be restored by calling <see cref="ClearToolTip"/>.
		/// </remarks>
		/// <param name="frameworkElement">The <see cref="FrameworkElement"/> to clear the <see cref="FrameworkElement.ToolTip"/> from.</param>
		/// <param name="toolTipText">The text to display in the <see cref="FrameworkElement.ToolTip"/>.</param>
		public void DisplayToolTip(FrameworkElement frameworkElement, string toolTipText)
		{
			if (!savedToolTips.ContainsKey(frameworkElement))
			{
				savedToolTips.Add(frameworkElement, (string)frameworkElement.ToolTip);
			}
			frameworkElement.ToolTip = toolTipText;
		}

		/// <summary>
		/// Clear the <see cref="FrameworkElement.ToolTip"/> from a <see cref="FrameworkElement"/>.
 		/// </summary>
 		/// <remarks>
 		/// Will set it back to the tooltip from when <see cref="DisplayToolTip"/> was called.
 		/// </remarks>
		/// <param name="frameworkElement">The <see cref="FrameworkElement"/> to clear the <see cref="FrameworkElement.ToolTip"/> from.</param>
		public void ClearToolTip(FrameworkElement frameworkElement)
		{
			if (savedToolTips.ContainsKey(frameworkElement))
			{
				frameworkElement.ToolTip = savedToolTips[frameworkElement];
				savedToolTips.Remove(frameworkElement);
			}
			else
			{
				frameworkElement.ToolTip = null;
			}
		}

		/// <summary>
		/// Displays the <see cref="Validation.ErrorTemplateProperty"/> for a <see cref="FrameworkElement"/>.
		/// </summary>
		/// <param name="frameworkElement">The <see cref="FrameworkElement"/> to show the <see cref="Validation.ErrorTemplateProperty"/> for.</param>
		public static void DisplayValidationAdorner(FrameworkElement frameworkElement)
		{
			method.Invoke(null, new object[] { frameworkElement, true });
		}

		/// <summary>
		/// Clears the <see cref="Validation.ErrorTemplateProperty"/> for a <see cref="FrameworkElement"/>.
		/// </summary>
		/// <param name="frameworkElement">The <see cref="FrameworkElement"/> to clear the <see cref="Validation.ErrorTemplateProperty"/> for.</param>
		public static void ClearValidationAdorner(FrameworkElement frameworkElement)
		{
			method.Invoke(null, new object[] { frameworkElement, false });
		}

	}
}
